home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 / Ham Radio 2000.iso / ham2000 / qex / qexbert / comport.c < prev    next >
C/C++ Source or Header  |  1994-09-18  |  7KB  |  343 lines

  1. /*  Functions to provide interrupt-driven serial I/O for the IBM PC.
  2.     From May, 1989 issue of _Personal Engineering_ magazine.
  3. */
  4.  
  5. #include <stdio.h>
  6. #include <alloc.h>
  7. #include <bios.h>
  8. #include <dos.h>
  9. #include <string.h>
  10.  
  11. #include "comport.h"
  12.  
  13. #define timeout 10000        /* Read_ln timeour, millisec */
  14.  
  15. #define thr (com->base_addr)
  16. #define rbr (com->base_addr)
  17. #define ier (com->base_addr+1)
  18. #define lcr (com->base_addr+3)
  19. #define mcr (com->base_addr+4)
  20. #define lsr (com->base_addr+5)
  21. #define msr (com->base_addr+6)
  22.  
  23. static COMM *ports[4] = {NULL, NULL, NULL, NULL};
  24.  
  25. int comm_errno;
  26.  
  27. void com_isr(COMM *com);
  28.  
  29. static void interrupt
  30. isr1(void)
  31. {
  32.     com_isr(ports[0]);
  33. }
  34.  
  35. static void interrupt
  36. isr2(void)
  37. {
  38.     com_isr(ports[1]);
  39. }
  40.  
  41. static void interrupt
  42. isr3(void)
  43. {
  44.     com_isr(ports[2]);
  45. }
  46.  
  47. static void interrupt
  48. isr4(void)
  49. {
  50.     com_isr(ports[3]);
  51. }
  52.  
  53. static void interrupt (*isrs[])() = {isr1, isr2, isr3, isr4};
  54.  
  55. void
  56. com_isr(COMM *com)
  57. {
  58.     com->buffer[com->buffer_in++] = inportb(rbr);    /* Get/store input byte */
  59.  
  60.     if (com->buffer_in == com->max_buffer)    /* Wrap input pointer */
  61.         com->buffer_in = 0;
  62.     com->buffer_length++;
  63.     if (com->buffer_length > com->max_buffer)
  64.     {                                /* Buffer overflow */
  65.         com->buffer_length = com->max_buffer;
  66.         com->overrun_flag = 1;
  67.     }
  68.     if (com->shake_type && !com->bf_hndshk)
  69.     {
  70.         if (com->buffer_length > com->near_full)
  71.         {                                /* Handshake off */
  72.             if (com->shake_type & CM_RTS)
  73.                 outportb(mcr, 9);
  74.             if (com->shake_type & CM_XOFF)
  75.             {
  76.                 while ((inportb(lsr) & 0x20) == 0)
  77.                     ;
  78.                 outportb(thr, 0x13);
  79.             }
  80.             com->bf_hndshk = 1;                /* flag true */
  81.         }
  82.     }
  83.     outportb(0x20, 0x20);                /* EOI to 8279 */
  84. }
  85.  
  86.  
  87. /* Reset (empty) input buffer */
  88. void
  89. reset_buffer(COMM *com)
  90. {
  91.     disable();
  92.     com->bf_hndshk = 0;
  93.     com->buffer_in = 0;
  94.     com->buffer_out = 0;
  95.     com->buffer_length = 0;
  96.     com->overrun_flag = 0;
  97.     enable();
  98. }
  99.  
  100. COMM *
  101. open_com(int cport,            /* COM port, 1 or 2 */
  102.     unsigned int baud,        /* Baud rate, 110-38400 */
  103.     int parity,            /* 0 = no parity, 1 = odd, 2 = even */
  104.     int stopbits,            /* Stop bits, 1 or 2 */
  105.     int numbits,            /* # data bits, 7 or 8 */
  106.     int shaketype,            /* 0 = no handshake, 1 = RTS handshake */
  107.     unsigned int buflen)    /* Size of receive buffer */
  108. {
  109.     int comdata;
  110.     int portidx;
  111.     int divisor;
  112.     int ptemp;
  113.     COMM *com;
  114.  
  115.     if (numbits < 7 || numbits > 8 || stopbits < 1 || stopbits > 2 ||
  116.         parity < 0 || parity > 3 || baud < 50 || baud > 38400)
  117.     {
  118.         comm_errno = CM_INVALID;
  119.         return NULL;
  120.     }
  121.     for (portidx = 0; portidx < sizeof ports / sizeof ports[0]; portidx++)
  122.         if (ports[portidx] == NULL)
  123.             break;
  124.     if (portidx == sizeof ports / sizeof ports[0])
  125.     {
  126.         comm_errno = CM_TOOMANY;
  127.         return NULL;
  128.     }
  129.     comm_errno = 0;
  130.     if ((com = calloc(1, sizeof (COMM))) == NULL)
  131.     {
  132.         comm_errno = CM_NOMEMORY;
  133.         return NULL;
  134.     }
  135.     if ((com->buffer = malloc(buflen)) == NULL)
  136.     {
  137.         comm_errno = CM_NOMEMORY;
  138.         free(com);
  139.         return NULL;
  140.     }
  141.     com->port = cport;
  142.     com->portidx = portidx;
  143.     ports[portidx] = com;
  144.     com->near_full = ((long) buflen * 9L) / 10L;
  145.     com->near_empty = com->near_full / 9;
  146.     com->max_buffer = buflen;
  147.     com->shake_type = shaketype;
  148.     comdata = 0;
  149.     comdata |= numbits - 5;
  150.     comdata |= (stopbits -1) << 2;
  151.     comdata |= parity << 3;
  152.     divisor = 115200L / (long) baud;
  153.     switch (cport)
  154.     {
  155.     default:
  156.     case 1:
  157.         com->base_addr = 0x3f8;
  158.         com->intlev = 0xc;
  159.         break;
  160.     case 2:
  161.         com->base_addr = 0x2f8;
  162.         com->intlev = 0xb;
  163.         break;
  164.     case 3:
  165.         com->base_addr = 0x3e8;
  166.         com->intlev = 0xc;
  167.         break;
  168.     case 4:
  169.         com->base_addr = 0x2e8;
  170.         com->intlev = 0xb;
  171.         break;
  172.     }
  173.     com->oldfunc = getvect(com->intlev);
  174.     setvect(com->intlev, isrs[portidx]);
  175.     disable();
  176.     ptemp = inportb(lcr) & 0x7f;
  177.     outportb(lcr, ptemp | 0x80);
  178.     outportb(thr, divisor);
  179.     outport(ier, divisor >> 8);
  180.     outportb(lcr, comdata);
  181.     inportb(lsr);            /* Reset any errors */
  182.     inportb(rbr);
  183.     switch (cport)
  184.     {
  185.     default:
  186.     case 1:
  187.     case 3:
  188.         outportb(0x21, inport(0x21) & 0xef);
  189.         break;
  190.     case 2:
  191.     case 4:
  192.         outportb(0x21, inportb(0x21) & 0xf7);
  193.         break;
  194.     }
  195.     outportb(ier, 1);            /* Enable data-ready interrupt */
  196.     outportb(mcr, 0xb);            /* RTS, DTR on, IRQ enabled */
  197.     reset_buffer(com);
  198. printf("Using COM%d, IRQ %d, divisor=%d\n", com->port, com->intlev-8, divisor);
  199.     return com;
  200. }
  201.  
  202. int
  203. com_in(COMM *com)
  204. {
  205.     int c;
  206.  
  207.     comm_errno = 0;
  208.     if (com->buffer_length == 0)
  209.         return -1;
  210.     if (com->overrun_flag)
  211.     {
  212.         comm_errno = CM_OVERRUN;
  213.         com->overrun_flag = 0;
  214.         return -1;
  215.     }
  216.     c = com->buffer[com->buffer_out++] & 0xff;
  217.     if (com->buffer_out == com->max_buffer)
  218.         com->buffer_out = 0;
  219.     com->buffer_length--;
  220.     if (com->shake_type && com->bf_hndshk)
  221.     {
  222.         if (com->buffer_length < com->near_empty)
  223.         {
  224.             if (com->shake_type & CM_RTS)
  225.                 outportb(mcr, 0xb);
  226.             if (com->shake_type & CM_XOFF)
  227.             {
  228.                 while ((inportb(lsr) & 0x20) == 0)
  229.                     ;
  230.                 outportb(thr, 0x11);
  231.             }
  232.             com->bf_hndshk = 0;
  233.         }
  234.     }
  235.     return c;
  236. }
  237.  
  238. int
  239. send_com(COMM *com,
  240.     char c,                /* Character to send */
  241.     int handshake)        /* Handshake type, 0=none, 1=CTS, 2=DSR, 3=CTS/DSR */
  242. {
  243.     int counter;
  244.     int shakemask;
  245.  
  246.     counter = 0;
  247.     comm_errno = 0;
  248.     switch (handshake)
  249.     {
  250.     default:
  251.         shakemask = 0;
  252.         break;
  253.     case 1:
  254.         shakemask = 0x10;
  255.         break;
  256.     case 2:
  257.         shakemask = 0x20;
  258.         break;
  259.     case 3:
  260.         shakemask = 0x30;
  261.         break;
  262.     }
  263.     do
  264.     {
  265.         if ((inportb(msr) & shakemask) == shakemask)
  266.             break;
  267.         if (counter >= timeout)
  268.         {
  269.             comm_errno = CM_TIMEOUT;
  270.             return -1;
  271.         }
  272.         counter++;
  273.         delay(1);
  274.     } while (1);
  275.     while ((inportb(lsr) & 0x20) == 0)
  276.         ;
  277.     outportb(thr, c);
  278.     return 0;
  279. }
  280.  
  281. int
  282. comm_cd(COMM *com)
  283. {
  284.     return (inportb(msr) & 0x80) ? 1 : 0;
  285. }
  286.  
  287. int
  288. comm_cts(COMM *com)
  289. {
  290.     return (inportb(msr) & 0x10) ? 1 : 0;
  291. }
  292.  
  293. int
  294. comm_dsr(COMM *com)
  295. {
  296.     return (inportb(msr) & 0x20) ? 1 : 0;
  297. }
  298.  
  299. void
  300. shut_down(COMM *com)
  301. {
  302.     outportb(mcr, 0);
  303. }
  304.  
  305. void
  306. close_com(COMM *com)
  307. {
  308.     if (com == NULL)
  309.         return;
  310.     disable();
  311.     outportb(0x21, inportb(0x21) | 0x18);
  312.     outportb(lcr, inportb(lcr) & 3);
  313.     outportb(ier, 0);
  314.     setvect(com->intlev, com->oldfunc);
  315.     enable();
  316.     ports[com->portidx] = NULL;
  317.     free(com->buffer);
  318.     free(com);
  319. }
  320.  
  321. char *
  322. comm_errmsg(int errno)
  323. {
  324.     static char *errmsg[] = {
  325.         "No error",
  326.         "Insufficient memory",
  327.         "Too many comm ports open",
  328.         "Invalid argument",
  329.         "Receiver overrun",
  330.         "Transmitter timeout"
  331.     };
  332.  
  333.     if (errno < 0 || errno > CM_NERRCODE)
  334.         return "Invalid COMM error number";
  335.     return errmsg[errno];
  336. }
  337.  
  338. void
  339. comm_perror(char *device)
  340. {
  341.     fprintf(stderr, "%s: %s\n", device, comm_errmsg(comm_errno));
  342. }
  343.